package com.weijian.util;

import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Redis分布式锁工具类
 * 基于Redis的SET NX PX命令实现，支持自动续期
 *
 * @author weijian
 */
@Component
public class RedisLockUtil {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private static final String LOCK_PREFIX = "lock:";
    private static final long DEFAULT_TIMEOUT = 30; // 默认30秒超时
    private static final long DEFAULT_EXPIRE = 30; // 默认30秒过期

    /**
     * 尝试获取锁
     *
     * @param key     锁的key
     * @param timeout 获取锁的超时时间（秒）
     * @return 锁标识（解锁时需要），获取失败返回null
     */
    public String tryLock(String key, long timeout) {
        String lockKey = LOCK_PREFIX + key;
        String lockValue = UUID.randomUUID().toString();
        long endTime = System.currentTimeMillis() + timeout * 1000;

        while (System.currentTimeMillis() < endTime) {
            Boolean locked = redisTemplate.opsForValue()
                    .setIfAbsent(lockKey, lockValue, DEFAULT_EXPIRE, TimeUnit.SECONDS);

            if (Boolean.TRUE.equals(locked)) {
                return lockValue;
            }

            // 短暂休眠后重试
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return null;
            }
        }

        return null; // 获取锁失败
    }

    /**
     * 尝试获取锁（使用默认超时时间）
     */
    public String tryLock(String key) {
        return tryLock(key, DEFAULT_TIMEOUT);
    }

    /**
     * 释放锁
     *
     * @param key       锁的key
     * @param lockValue 锁标识
     * @return 是否释放成功
     */
    public boolean unlock(String key, String lockValue) {
        String lockKey = LOCK_PREFIX + key;

        // 使用Lua脚本确保原子性：只有当值匹配时才删除key
        String script =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "return redis.call('del', KEYS[1]) " +
                        "else return 0 end";

        Long result = redisTemplate.execute(
                new DefaultRedisScript<>(script, Long.class),
                Collections.singletonList(lockKey),
                lockValue
        );

        return result != null && result > 0;
    }

    /**
     * 检查锁是否存在
     */
    public boolean isLocked(String key) {
        String lockKey = LOCK_PREFIX + key;
        return Boolean.TRUE.equals(redisTemplate.hasKey(lockKey));
    }

    /**
     * 获取锁的剩余时间（秒）
     */
    public long getRemainingTime(String key) {
        String lockKey = LOCK_PREFIX + key;
        Long expire = redisTemplate.getExpire(lockKey, TimeUnit.SECONDS);
        return expire != null ? expire : 0;
    }
}